﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Common.Reflection;

namespace Common.Exceptional.Routing
{
    public class ExceptionConfiguration
    {
        public static readonly ExceptionConfiguration Current = new ExceptionConfiguration();

        public readonly List<ExceptionRouteInfo> RouteInfos = new List<ExceptionRouteInfo>();

        public ExceptionConfiguration Register(Assembly assembly, string @namespace = null)
        {
            var handlerTypes = assembly.GetExportedTypes()
                .Where(i => @namespace == null || i.Namespace == @namespace);
            handlerTypes.Foreach(r =>
            {
                var handerDef = typeof(IExceptionHandler<>);
                var interfaces = r.GetInterfaces()
                    .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == handerDef);
                interfaces.Foreach(itf =>
                {
                    var exceptionType = itf.GetGenericArguments().Single();
                    var attr = r.GetImplementOfInterfaceMethod(itf.GetMethods().Single())
                        .GetCustomAttribute<ExceptionHandlingAttribute>();
                    var handlerOrder = attr.IfNotNull(i => i.Order);
                    var channel = attr.IfNotNull(i => i.Channel);
                    var handerInfo = new ExceptionHandlerInfo
                    {
                        HandleOrder = handlerOrder,
                        HandlerType = r,
                        HandlerChannel = channel
                    };
                    Register(exceptionType, handerInfo);
                });
            });
            return this;
        }

        public ExceptionConfiguration Register(Type exceptionType, ExceptionHandlerInfo handlerInfo)
        {
            ExceptionRouteInfo routeInfo;
            lock (exceptionType)
            {
                routeInfo = RouteInfos.FirstOrDefault(i => i.ExceptionType == exceptionType);
                if (routeInfo == null)
                {
                    routeInfo = new ExceptionRouteInfo { ExceptionType = exceptionType };
                    RouteInfos.Add(routeInfo);
                }
            }
            lock (handlerInfo.HandlerType)
            {
                if (routeInfo.ExceptionHandlers.All(i => i.HandlerType != handlerInfo.HandlerType))
                {
                    routeInfo.ExceptionHandlers.Add(handlerInfo);
                }
            }
            return this;
        }
    }
}